package org.softee.functional.operation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;

public class Transposals {

    /**
     * Intentionally uninstantiable
     */
    private Transposals() {

    }

    /**
     * Workaround to generics type inferance problem
     * @param clazz
     * @return
     */
    public static <T> Transposal<T> reverse(Class<T> clazz) {
        return reverse();
    }

    public static <T> Transposal<T> reverse() {
        return new Transposal<T>() {
            @Override
            public Collection<T> apply(Collection<T> data) {
                List<T> list = new ArrayList<T>(data);
                Collections.reverse(list);
                return list;
            }
        };
    }

    public static Transposal<String> sortCaseInsensitive() {
        return sort(String.CASE_INSENSITIVE_ORDER);
    }

    public static <T> Transposal<T> sort() {
        return new Transposal<T>() {
            @Override
            public Collection<T> apply(Collection<T> data) {
                return doSort(data, null);
            }
        };
    }

    /**
     * NOTE: nulls are NOT allowed in the collection.
     * @param comparator (may be null) the comparator to use when sorting
     * @return a transposal that sorts a collection according to the order specified by comparator, or the natural
     * order of elements, if the comparator is null
     */
    public static <T> Transposal<T> sort(final Comparator<? super T> comparator) {
        return new Transposal<T>() {
            @Override
            public Collection<T> apply(Collection<T> data) {
                return doSort(data, comparator);
            }
        };
    }

    public static <T> Transposal<T> unique() {
        return new Transposal<T>() {
            @Override
            public Collection<T> apply(Collection<T> inputs) {
                // maintain insertion order - hence LinkedHashSet()
                return new LinkedHashSet<T>(inputs);
            }
        };
    }

    private static <T> Collection<T> doSort(Collection<T> data, Comparator<? super T> comparator) {
        ArrayList<T> arrayList = (data instanceof ArrayList) ? (ArrayList<T>)data : new ArrayList<T>(data);
        @SuppressWarnings("unchecked")
        T[] array = (T[]) arrayList.toArray();
        if (comparator != null) {
            Arrays.sort(array, comparator);
        } else {
            Arrays.sort(array);
        }
        return Arrays.asList(array);
    }
}
